package com.example.mybatis.handler.result;

import com.example.mybatis.config.Config;
import com.example.mybatis.exception.MybatisException;
import com.example.mybatis.handler.type.handler.TypeHandler;
import com.example.mybatis.mapper.MapperClass;
import com.example.mybatis.mapper.MapperMethod;
import com.example.mybatis.mapper.MapperResultItemTemplate;
import com.github.houbb.heaven.util.lang.ObjectUtil;
import com.github.houbb.heaven.util.lang.StringUtil;
import com.github.houbb.heaven.util.lang.reflect.ReflectMethodUtil;
import com.github.houbb.heaven.util.util.CollectionUtil;

import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ResultHandler {
    /**
     * 结果类型
     *
     * @since 0.0.1
     */
    private final Class<?> resultType;

    /**
     * 方法返回类型
     *
     * @since 0.1.10
     */
    private final Class<?> methodReturnType;

    /**
     * 配置信息
     *
     * @since 0.0.4
     */
    private final Config config;

    /**
     * 对应的方法信息
     *
     * @since 0.0.12
     */
    private final Method method;

    final MapperMethod mapperMethod;

    public ResultHandler(Config config, final MapperMethod mapperMethod) {
        this.resultType = mapperMethod.getResultType();
        this.methodReturnType = mapperMethod.getMethod().getReturnType();
        this.config = config;
        this.method = mapperMethod.getMethod();
        this.mapperMethod = mapperMethod;
    }

    public Object buildResult(final ResultSet resultSet) throws SQLException {
        List<Object> resultList = new ArrayList<>();
        //结果大小的判断
        //为空直接返回，大于1则报错
        while (resultSet.next()) {
            //分成两种情况
            Object value = getValueByResultType(resultSet);
            resultList.add(value);
        }
        //如果结果为列表，则直接返回
        //这里暂时不考虑各种子类的情况
        if (List.class.equals(methodReturnType)) {
            return resultList;
        }
        //结果集合校验，后期优化为提前失败
        if (resultList.size() > 1) {
            throw new MybatisException("Expect one, but found " + resultList.size());
        }
        if (CollectionUtil.isEmpty(resultList)) {
            return null;
        }
        //返回第一个
        return resultList.get(0);
    }

    private Object getValueByResultType(final ResultSet resultSet) {
        try {
            if (resultSet.wasNull()) {
                return null;
            }
            //基本类型
            TypeHandler<?> typeHandler = config.getTypeHandler(resultType);
            if (ObjectUtil.isNotNull(typeHandler)) {
                return typeHandler.getResult(resultSet, 1);
            }
            ResultHandlerContext context = ResultHandlerContext.newInstance().config(config).resultMapId(mapperMethod.getResultMap()).resultMapMapping(getResultMapMapping()).resultSet(resultSet);

            //特殊处理返回值
            Class<?> actualResultType = this.resultType;
            if (ObjectUtil.isNull(actualResultType)) {
                actualResultType = getResultTypeByMethod();
            }
            context.resultType(actualResultType);
            //Map
            if (Map.class.equals(resultType)) {
                return MapResultTypeHandler.getInstance().buildResult(context);
            }
            //引用对象
            return ObjectRefResultTypeHandler.getInstance().buildResult(context);

        } catch (SQLException ex) {
            throw new MybatisException(ex);
        }
    }

    /**
     * 根绝方法的返回值，获取元素对应的结果
     *
     * @return 结果类型
     * @since 0.0.12
     */
    private Class<?> getResultTypeByMethod() {
        if (Map.class.equals(methodReturnType)) {
            return Map.class;
        }
        if (List.class.equals(methodReturnType)) {
            //根据泛型对应的元素
            return ReflectMethodUtil.getGenericReturnParamType(method, 0);
        }
        //返回本身
        return methodReturnType;
    }

    /**
     * 获取结果映射列表
     *
     * @return
     */
    private List<MapperResultItemTemplate> getResultMapMapping() {
        String resultMap = mapperMethod.getResultMap();
        if (StringUtil.isEmpty(resultMap)) {
            return null;
        }
        final MapperClass mapperClass = mapperMethod.getRefClass();
        for (Map.Entry<String, List<MapperResultItemTemplate>> entry : mapperClass.getResultMapMapping().entrySet()) {
            String name = entry.getKey();
            if (resultMap.equals(name)) {
                return entry.getValue();
            }
        }
        throw new MybatisException("Not found result map for name: " + resultMap);
    }
}
